################################################################################
#           This is a file containing many types of awesome dicts!             #
################################################################################
import re

class Sdict(dict):
    def __getitem__(self, item):
        rval = [x[1] for x in self.sitems() if x[0] == item]
        oval = [x[1] for x in self.sitems() if str(x[0]).lower() == str(item).lower() and x[0] != item]
        if rval:
            return rval[0]
        if oval:
            return oval[0]
        raise KeyError

    def __call__(self, item):
        rval = [x[1] for x in self.sitems() if x[0] == item]
        oval = [x[1] for x in self.sitems() if str(x[0]).lower() == str(item).lower() and x[0] != item]
        aval = rval + oval
        if not aval:
            raise KeyError
        return aval
                   
    def sitems(self):
        return set(self.items())

    def intersection(self, sdic):
        if not isinstance(sdic, Sdict):
            if isinstance(sdic, dict):
                sdic = Sdict(sdic)
            else:
                raise TypeError
        return Sdict(self.sitems().intersection(sdic.sitems()))

    def difference(self, sdic):
        if not isinstance(sdic, Sdict):
            if isinstance(sdic, dict):
                sdic = Sdict(sdic)
            else:
                raise TypeError
        return Sdict(self.sitems().difference(sdic.sitems()))

    def union(self, sdic):
        if not isinstance(sdic, Sdict):
            if isinstance(sdic, dict):
                sdic = Sdict(sdic)
            else:
                raise TypeError
        return Sdict(self.sitems().union(sdic.sitems()))

    def update(self, lst):
        if (isinstance(lst, list) or isinstance(lst, tuple) or isinstance(lst, set)) and not [x for x in lst if not isinstance(x, tuple) or len(x) != 2]:
            for x in lst:
                self[x[0]] = x[1]
        elif isinstance(lst, dict) or isinstance(lst, Sdict):
            self.update(lst.items())


class Rdict(dict):
    def __getitem__(self, item):
        if item in self.keys():
            return dict(self.items())[item]
        vkeys = [y[0] for y in [re.compile(str(item).lower()).findall(str(x).lower()) for x in self.keys()] if y]
        if vkeys:
            return [x[1] for x in self.items() if vkeys.__contains__(str(x[0]).lower())][0]
        raise KeyError
        

    def __call__(self, item):
        if item in self.keys():
            return dict(self.items())[item]
        vkeys = [y[0] for y in [re.compile(str(item).lower()).findall(str(x).lower()) for x in self.keys()] if y]
        if vkeys:
            return [x[1] for x in self.items() if vkeys.__contains__(str(x[0]).lower())]
        raise KeyError
        



    

    
